﻿using DGZImp.ApsProject.DomainModels;
using DGZImp.ApsProject.IServices;
using DGZImp.BasProject.DomainModels;
using DGZImp.Core.DbSqlSugar;
using DGZImp.Core.EFDbContext;
using DGZImp.Core.Exceptions;
using DGZImp.Core.Extensions;
using DGZImp.Core.ManageUser;
using DGZImp.Core.Utilities;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SqlSugar;
using System.Net.Http.Json;
using System.Reflection.Metadata;
using System.Text;

namespace DGZImp.WmsProject.Services
{

    /// <summary>
    /// Aps同步job
    /// </summary>
    public class ApsAmesJobService : IDependency, IApsAmesJobService
    {
        public ISqlSugarClient Db = AutofacContainerModule.GetService<ServiceDbContext>()?.SqlSugarClient;
        public UserInfo userInfo = UserContext.Current.UserInfo;

        public ILogger<ApsAmesJobService> logger { get; set; }

        //private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

        public IConfiguration _config;

        public string url = "";
        public string db = "";
        public string userName = "";
        public string password = "";

        public static int? uid = null;
        public static string sessionId = null;
        public int flag = 0;
        public static HttpClient _client = null;

        public ApsAmesJobService(IConfiguration configuration, IHttpClientFactory httpClientFactory)
        {
            _config = configuration;
            if (_client == null) _client = httpClientFactory.CreateClient();
            this.url = _config["Interfaces:AnTaiMes:Url"];
            this.db = _config["Interfaces:AnTaiMes:DB"];
            this.userName = _config["Interfaces:AnTaiMes:UserName"];
            this.password = _config["Interfaces:AnTaiMes:Password"];
        }


        public async Task<string> Test()
        {
            return "123";
        }

        public async Task<JObject> Call(string service, string method, List<dynamic> args)
        {

            var json = new
            {
                jsonrpc = "2.0",
                method = "call",
                @params = new { service = service, method = method, args = args },
                id = Guid.NewGuid(),
            };
            try
            {
                var content = new StringContent(JsonConvert.SerializeObject(json), Encoding.UTF8, "application/json");
                var test = JsonConvert.SerializeObject(json);
                //VOL.Core.Services.Logger.Info("ceshi--" + test);
                var response = await _client.PostAsync(url, content);
                response.EnsureSuccessStatusCode();
                var responseString = await response.Content.ReadAsStringAsync();
                var responseJson = JObject.Parse(responseString);
                //if (responseJson.ContainsKey("error"))
                //{
                //    throw new Exception(responseJson["error"].ToString());
                //}
                //responseJson["result"]
                return responseJson;
            }
            catch (Exception e)
            {
                throw new AppException(e.Message);
            }
        }

        /// <summary>
        /// 获取odoo平台用户id
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public async Task<int?> GetOdooUid(string username, string password)
        {
            var res = await Call("common", "login", new List<dynamic> { db, username, password });
            return Convert.ToInt32(res["result"]);
        }

        /// <summary>
        /// 具体业务调用
        /// 如果报错则会再调用一次
        /// </summary>
        /// <param name="service"></param>
        /// <param name="method"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public async Task<JObject> BusinessCall(string service, string method, List<dynamic> args)
        {

            JObject data = null;

            try
            {
                //if (uid == null || uid == 0) 
                    uid = await GetOdooUid(userName, password);
                args[1] = uid;
                data = await Call(service, method, args);
                //DGZImp.Core.log.Log();
            }
            catch (Exception ex)
            {
                if (flag > 0) throw new AppException(ex.ToString());

                flag = flag + 1;
                //重新获取用户id
                uid = null;
                data = await BusinessCall(service, method, args);
            }

            return data;


        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="functionName">方法名</param>
        /// <param name="intPeriod">间隔时间</param>
        /// <returns></returns>
        private async Task<Wms_SyncdataLog> GetSyncTimestamp(string functionName, int intPeriod = 3)
        {
            var runStartTime = DateTime.Now;
            var data = new Wms_SyncdataLog() { FunctionName = functionName, RunStartTime = runStartTime, LogStatus = 0 };
            var log = await Db.Queryable<Wms_SyncdataLog>()
                    .Where(x => x.FunctionName == functionName && x.IsValid == 1).FirstAsync();
            if (log == null)
            {
                data.SyncDataLogId = new IdWorker(1L, 1L).NextId();
                Db.DoInsert(data);

                log = await Db.Queryable<Wms_SyncdataLog>()
                    .Where(x => x.FunctionName == functionName && x.IsValid == 1).FirstAsync();
            }

            data.MaxExeTimes = log.MaxExeTimes.IsValuable() ? log.MaxExeTimes : data.MaxExeTimes;
            data.SyncDataLogId = log.SyncDataLogId;
            data.RunStartTime = runStartTime;

            if (log.LogStatus == 0 && (log.DataTimestamp != null &&
                DateTime.Now.Subtract((DateTime)log.DataTimestamp).TotalHours <= log.MaxExeTimes))
                return null;

            if (log.LogStatus == 1 || log.LogStatus == 2)
                await Db.DoUpdateAsync<Wms_SyncdataLog>(new { LogStatus = 0, RunStartTime = runStartTime, LogMessage = "" },
                    a => a.SyncDataLogId == log.SyncDataLogId);

            var lastSyncTime = DateTime.Now.AddDays(0 - intPeriod);  //没有默认取3天之前00:00:00
            lastSyncTime = log.DataTimestamp == null ? lastSyncTime : ((DateTime)log.DataTimestamp).AddMinutes(-200);
            data.SyncStartTime = lastSyncTime;

            return data;
        }

        /// <summary>
        /// 同步作业完成 记录日志
        /// </summary>
        /// <param name="functionName"></param>
        /// <param name="lastTime"></param>
        /// <returns></returns>
        public async Task UpdateSyncdataLog(Wms_SyncdataLog log)
        {
            log.RunEndTime = DateTime.Now;
            var TimeUsed = (int)((log.RunEndTime.Value.Subtract((DateTime)log.RunStartTime)).TotalSeconds);
            await Db.DoUpdateAsync<Wms_SyncdataLog>(new { LogStatus = (log.LogStatus == null ? 1 : 2), RunEndTime = log.RunEndTime, SyncStartTime = log.SyncStartTime, SyncEndTime = log.SyncEndTime, DataTimestamp = log.SyncEndTime, TimeUsed = TimeUsed, LogMessage = log.LogMessage }, a => a.SyncDataLogId == log.SyncDataLogId);
        }



        public async Task<string> SyncSaleOrderAndMo()
        {
            try
            {
                await SyncSalesOrder();
                await SyncMo();
                return "同步成功";
            }
            catch (Exception ex)
            {

                throw new AppException(ex.Message);
            }
        }

        /// <summary>
        /// 同步产品含有Ce
        /// </summary>
        /// <returns></returns>
        public async Task<string> SyncMaterialHasCe()
        {

            var requestParams = new List<dynamic> { db, uid, password };
            var functionName = $"ApsAmesJobService_SyncMaterialHasCe";
            var period = Convert.ToInt32(_config["Interfaces:AnTaiMes:SyncMaterialPeriod"]);
            var log = await GetSyncTimestamp(functionName, period);
            if (log == null) return "有任务正在运行中";
            log.SyncStartTime = log.SyncStartTime.Value.AddMinutes(-100);
            string message = string.Empty;
            var argsJson = $"[[[\"write_date\",\">\",\"{log.SyncStartTime.ToString("yyyy-MM-dd H:mm:ss")}\"]" +
                $"],[\"name\",\"magnet_process_code\",\"grade\",\"state\"]]";
            var argsJarray = JsonConvert.DeserializeObject<JArray>(argsJson);

            requestParams.Add("atm.alloy");
            requestParams.Add("search_read");
            requestParams.Add(argsJarray);
            requestParams.Add(new { context = new { lang = "zh_CN", tz = "Asia/Shanghai" }, order = "write_date asc" });
            var time = DateTime.Now;
            Db.Ado.BeginTran();
            try
            {
                var resp = await BusinessCall("object", "execute_kw", requestParams);
                if (resp.ContainsKey("error"))
                {
                    message = resp["error"].ToString();
                    throw new AppException(message);
                }
                var res = (JArray)resp["result"];
                List<string> list = new List<string>();

                if (res == null || res.Count <= 0) goto next;

                var namesHasCe = new List<string>() { };
                var namesNoHasCe = new List<string>() { };
                foreach (var item in res)
                {
                    if (item["magnet_process_code"].ToString() == "C")
                    {
                        namesHasCe.Add(item["name"].ToString());
                    }
                    else
                    {
                        namesNoHasCe.Add(item["name"].ToString());
                    }
                }


                await Db.DoUpdateAsync<Bas_Material>(new { IsCe = 1 }, a => namesHasCe.Contains(a.MaterialName));
                await Db.DoUpdateAsync<Bas_Material>(new { IsCe = 0 }, a => namesNoHasCe.Contains(a.MaterialName));
            next:
                log.SyncEndTime = time;
                await UpdateSyncdataLog(log);
                Db.Ado.CommitTran();
            }
            catch (Exception e)
            {
                message = e.Message;
                Db.Ado.RollbackTran();
                log.LogStatus = 2;
                log.LogMessage = e.Message;
                await UpdateSyncdataLog(log);
            }

            return message;
        }


        /// <summary>
        /// 同步生产订单
        /// </summary>
        /// <returns></returns>
        public async Task<string> SyncSalesOrder()
        {
            var OrderPullRules = await Db.Queryable<Bas_Parameter>()
                         .Where(it => it.ParamName == "OrderPullRule")
                         .Select(it => it.ParamValue)
                         .ToListAsync();
            if (OrderPullRules.Count == 0)
            {
                throw new AppException($"系统参数'OrderPullRule'未配置拉取的订单类型");
            }

            var requestParams = new List<dynamic> { db, uid, password };
            var functionName = $"ApsAmesJobService_SyncSalesOrder";
            var period = Convert.ToInt32(_config["Interfaces:AnTaiMes:SynSalescOrderPeriod"]);
            var log = await GetSyncTimestamp(functionName, period);
            if (log == null) return "有任务正在运行中";
            log.SyncStartTime = log.SyncStartTime.Value.AddMinutes(-100);
            string message = string.Empty;
            var argsJson = $"[[[\"write_date\",\">\",\"{log.SyncStartTime.ToString("yyyy-MM-dd H:mm:ss")}\"]" +
                $"],[\"name\",\"product_id\",\"product_qty\",\"reply_delivery_date\",\"id\",\"schedule_route\"]]";
            var argsJarray = JsonConvert.DeserializeObject<JArray>(argsJson);

            requestParams.Add("sale.order");
            requestParams.Add("search_read");
            requestParams.Add(argsJarray);
            requestParams.Add(new { context = new { lang = "zh_CN", tz = "Asia/Shanghai" }, order = "write_date asc" });
            var time = DateTime.Now;
            Db.Ado.BeginTran();
            try
            {
                var resp = await BusinessCall("object", "execute_kw", requestParams);
                if (resp.ContainsKey("error"))
                {
                    message = resp["error"].ToString();
                    throw new AppException(message);
                }
                var res = (JArray)resp["result"];
                List<string> list = new List<string>();




                //logger.LogDebug("物料同步历史记录保存:" + JsonConvert.SerializeObject(resp["result"]));

                if (res == null || res.Count <= 0) goto next;
                var codes = res.Select(a => a["name"].ToString()).ToList();



                var Orders = Db.Queryable<Aps_SalesOrder>()
                     .Where(a => codes.Contains(a.OrderNo))
                     .Where(a => a.IsValid == 1)
                     .ToList();

                var SalesOrderIds = Orders.Select(a => a.SalesOrderId).ToList();



                var insertLst = new List<Aps_SalesOrder>() { };
                var updateList = new List<Aps_SalesOrder>() { };


                var List = res.Where(a => OrderPullRules.Any(b => a["name"].ToString().StartsWith(b))).ToList();// 仅过滤系统参数中配置的可以拉取的订单信息

                foreach (var item in List)
                {

                    var tmp = Orders.Where(a => a.OrderNo == item["name"].ToString()).FirstOrDefault();
                    var materialCode = (string)(item["product_id"] is JArray ? ((JArray)(item["product_id"]))[1] : "");
                    var scheduleRoute = item["schedule_route"].ToString();
                    var curMaterialId = Db.Set<Bas_Material>().Where(a => a.MaterialName == materialCode)?.FirstOrDefault()?.MaterialId;

                    if (curMaterialId == null)  // 订单没有物料的过滤
                    {
                        continue;
                    }


                    /*    mto:"生产发货"
                          stock: "库存发货"
                          forecast: "预投发货"
                          oem: "OEM发货"*/
                    if (scheduleRoute != "mto")  // 发货路线不是“生产发货”的过滤
                    {
                        continue;
                    }

                    if (tmp == null)
                    {
                        insertLst.Add(new Aps_SalesOrder
                        {
                            OrderNo = item["name"].ToString(),
                            RequiredDate = item["reply_delivery_date"].Type == JTokenType.Boolean ? null : (DateTime)item["reply_delivery_date"],
                            MaterialId = curMaterialId,
                            TotalQty = (decimal)item["product_qty"],

                        });
                        continue;
                    }
                    else
                    {
                        tmp.RequiredDate = item["reply_delivery_date"].Type == JTokenType.Boolean ? null : (DateTime)item["reply_delivery_date"];
                        tmp.MaterialId = curMaterialId;
                        tmp.TotalQty = (decimal)item["product_qty"];
                        updateList.Add(tmp);
                    }
                }
                //logger.LogDebug("物料同步数据:" + JsonConvert.SerializeObject(lst));
                if (insertLst.Count > 0) await Db.DoInsertAsync(insertLst);

                if (updateList.Count > 0) await Db.DoUpdateAsync(updateList);
                next:
                log.SyncEndTime = time;
                await UpdateSyncdataLog(log);
                Db.Ado.CommitTran();
            }
            catch (Exception e)
            {
                message = e.Message;
                logger.LogError($"【同步订单数据】{e.Message}");
                Db.Ado.RollbackTran();
                log.LogStatus = 2;
                log.SyncEndTime = log.SyncEndTime == null ? time : log.SyncEndTime;
                log.LogMessage = e.Message;
                await UpdateSyncdataLog(log);
            }

            return message;
        }

        /// <summary>
        /// 同步工单
        /// </summary>
        /// <returns></returns>
        public async Task<string> SyncMo()
        {
            var requestParams = new List<dynamic> { db, uid, password };
            var functionName = $"ApsAmesJobService_SyncMo";
            var period = Convert.ToInt32(_config["Interfaces:AnTaiMes:SyncMoPeriod"]);
            var log = await GetSyncTimestamp(functionName, period);
            if (log == null) return "有任务正在运行中";
            log.SyncStartTime = log.SyncStartTime.Value.AddMinutes(-100);
            string message = string.Empty;
            var argsJson = $"[[[\"write_date\",\">\",\"{log.SyncStartTime.ToString("yyyy-MM-dd H:mm:ss")}\"]" +
                 $"],[\"schedule_id\",\"name\",\"raw_lot_id\",\"product_qty\",\"product_id\",\"date_delivery\",\"state\"]]";
            var argsJarray = JsonConvert.DeserializeObject<JArray>(argsJson);

            requestParams.Add("mrp.production");
            requestParams.Add("search_read");
            requestParams.Add(argsJarray);
            requestParams.Add(new { context = new { lang = "zh_CN", tz = "Asia/Shanghai" }, order = "write_date asc" });
            var time = DateTime.Now;
            Db.Ado.BeginTran();
            try
            {
                var resp = await BusinessCall("object", "execute_kw", requestParams);
                if (resp.ContainsKey("error"))
                {
                    message = resp["error"].ToString();
                    throw new AppException(message);
                }
                var res = (JArray)resp["result"];
                List<string> list = new List<string>();

                //logger.LogDebug("物料同步历史记录保存:" + JsonConvert.SerializeObject(resp["result"]));

                if (res == null || res.Count <= 0) goto next;
                var codes = res.Select(a => a["name"].ToString()).ToList();

                var Orders = Db.Queryable<Aps_Mo>()
                     .Where(a => codes.Contains(a.MoCode))
                     .Where(a => a.IsValid == 1)
                     .ToList();

                var MoIds = Orders.Select(a => a.MoId).ToList();

                var insertLst = new List<Aps_Mo>() { };
                var updateList = new List<Aps_Mo>() { };



                foreach (var item in res)
                {

                    var tmp = Orders.Where(a => a.MoCode == item["name"].ToString()).FirstOrDefault();
                    var materialCode = (string)(item["product_id"] is JArray ? ((JArray)(item["product_id"]))[1] : "");
                    var SalerOrderCode = (string)(item["schedule_id"] is JArray ? ((JArray)(item["schedule_id"]))[1] : "");

                    // 仅过滤气流磨/压坯/毛坯
                    if (!(materialCode.Contains("气流磨") || materialCode.Contains("压坯") || materialCode.Contains("毛坯")))
                    {
                        continue;
                    }

                    // 订单ID
                    long? curSalerOrderId = 0;

                    // 投料批次
                    var SoureMoCode = (string)(item["raw_lot_id"] is JArray ? ((JArray)(item["raw_lot_id"]))[1] : "");
                    // var SoureMoId = Db.Set<Aps_Mo>().Where(a => a.MoCode == SoureMoCode)?.FirstOrDefault()?.MoId;

                    if (string.IsNullOrEmpty(SalerOrderCode))
                    {
                        // 如果没有订单号，则根据投料批次去查询订单号
                        var SoureMoCodeInfo = res.Where(a => SoureMoCode == a["name"].ToString()).FirstOrDefault();
                        if (SoureMoCodeInfo != null)
                        {
                            SalerOrderCode = (string)(SoureMoCodeInfo["schedule_id"] is JArray ? ((JArray)(SoureMoCodeInfo["schedule_id"]))[1] : "");
                        }
                        else
                        {
                            // 如果根据投料批次还是找不到，就调用接口查询
                            var r = await GetSaleOrderByLotNo(item["name"].ToString());

                            var re = JsonConvert.DeserializeObject<dynamic>(r);
                            if (re.ContainsKey("error"))
                            {
                                SalerOrderCode = null;
                            }
                            var result = (JArray)re["result"];
                            if (result != null && result.Count > 0)
                            {
                                SalerOrderCode = result[0]["schedule_id"].Type == JTokenType.Boolean ? null : result[0]["schedule_id"][1].ToString();
                            }
                            else
                            {
                                SalerOrderCode = null;
                            }
                        }


                    }
                    curSalerOrderId = Db.Set<Aps_SalesOrder>().Where(a => a.OrderNo == SalerOrderCode)?.FirstOrDefault()?.SalesOrderId;

                    // 系统中没有的订单就不同步对应的工单
                    if (curSalerOrderId == null || curSalerOrderId == 0)
                    {
                        continue;
                    }
                    int status = (int)(MoStatus)Enum.Parse(typeof(MoStatus), item["state"].ToString());

                    if (tmp == null)
                    {
                        insertLst.Add(new Aps_Mo
                        {
                            MoCode = item["name"].ToString(),
                            SalesOrderId = curSalerOrderId,
                            LotNo = SoureMoCode,
                            Qty = (decimal)item["product_qty"],
                            ProductsCode = materialCode,
                            DeadLine = (DateTime)item["date_delivery"],
                            Status = status

                        });
                        continue;
                    }
                    else
                    {
                        tmp.Qty = (decimal)item["product_qty"];
                        tmp.DeadLine = (DateTime)item["date_delivery"];
                        tmp.LotNo = SoureMoCode;
                        tmp.ProductsCode = materialCode;
                        tmp.Status = status;
                        updateList.Add(tmp);
                    }
                }
                //logger.LogDebug("物料同步数据:" + JsonConvert.SerializeObject(lst));
                if (insertLst.Count > 0) await Db.DoInsertAsync(insertLst);

                if (updateList.Count > 0) await Db.DoUpdateAsync(updateList);
                next:
                log.SyncEndTime = time;
                await UpdateSyncdataLog(log);
                Db.Ado.CommitTran();
            }
            catch (Exception e)
            {
                message = e.Message;
                //logger.LogError($"【同步物料和产品】{e.Message}");
                Db.Ado.RollbackTran();
                log.LogStatus = 2;
                //log.SyncEndTime = log.SyncEndTime == null ? time : log.SyncEndTime;
                log.LogMessage = e.Message;
                await UpdateSyncdataLog(log);
            }

            return message;
        }

        // 定义工单状态枚举
        public enum MoStatus
        {
            draft = 0, // 新建
            cancel = 1, //已取消
            confirmed = 2, // 等待原材料
            ready = 3, // 准备生产
            in_production = 4, // 已开始生产
            produced = 5,//产出
            qc_pass = 6, //质检完成
            rework = 7, //返工
            done = 8 // 完成
        }


        public async Task<string> GetSaleOrderByLotNo(string code)
        {
            var requestParams = new List<dynamic> { db, uid, password };
            var argsJson = $"[[[\"raw_lot_id\",\"ilike\",\"{code}\"]" +
                $", [\"schedule_id\",\"name\",\"raw_lot_id\",\"product_qty\",\"product_id\",\"date_delivery\"]]";
            var argsJarray = JsonConvert.DeserializeObject<JArray>(argsJson);
            //requestParams.Add(new { context = new { lang = "zh_CN", tz = "Asia/Shanghai" } });
            requestParams.Add("mrp.production");
            requestParams.Add("search_read");
            requestParams.Add(argsJarray);
            requestParams.Add(new { order = "write_date asc" });
            var resp = await BusinessCall("object", "execute_kw", requestParams);

            return JsonConvert.SerializeObject(resp);
        }



        public async Task<string> GetOdooSessionId()
        {
            var loginRequest = new
            {
                jsonrpc = "2.0",
                method = "call",
                @params = new
                {
                    db = this.db,
                    login = this.userName,
                    password = this.password,
                }
            };
            _client.DefaultRequestHeaders.Clear();
            int index = this.url.LastIndexOf('/');
            var urltmp = this.url.Substring(0, index) + "/web/session/authenticate";
            HttpResponseMessage loginResponse = await _client.PostAsJsonAsync(urltmp, loginRequest);

            if (loginResponse.IsSuccessStatusCode)
            {
                var sessionInfo = await loginResponse.Content.ReadAsStringAsync();
                var obj = JObject.Parse(sessionInfo);
                if (!obj.ContainsKey("error"))
                {
                    return obj["result"]["session_id"].ToString();
                }
            }

            throw new AppException("获取sessionId失败");
        }





    }










}